﻿using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using EnvDTE;

namespace ScaffoldIt.Configuration
{    
    public class Template : IDescriptiveElement
    {
        [XmlAttribute]
        public string Name { get; set; }

        public string Description { get; set; }

        [XmlAttribute("project")]
        public string ProjectName { get; set; }

        [XmlAttribute("template")]
        public string TemplateName { get; set; }

        [XmlAttribute("output")]
        public string OutputName { get; set; }

        private List<Parameter> parameters = new List<Parameter>();
        [XmlArray("Params")]
        public List<Parameter> Parameters
        {
            get
            {
                return this.parameters;
            }
            set
            {
                this.parameters = value;
            }
        }

        private Project _project = null;
        private string _templatedElement = null;
        private string _fullpath = null;
        private string _fullfilename = null;

        internal string Tokenize(string source, ScaffoldingContext ctx, ScaffoldModel model)
        {
            var res = source;
            if (res != null)
            {
                res = res.Replace("{ClassName}", model.ClassName);
                res = res.Replace("{PluralizedClassName}", model.PluralizedClassName);
                res = res.Replace("{FullName}", model.FullName);
                res = res.Replace("{Namespace}", model.Namespace);
                res = res.Replace("{GeneratedNamespace}", model.GeneratedNamespace);

                if (ctx != null && ctx.Parameters != null)
                {
                    foreach (var parameter in ctx.Parameters)
                    {
                        res = res.Replace("{" + parameter.Code + "}", parameter.Value);
                    }
                }
            }

            return res;
        }

        internal void ApplyTemplate(ScaffoldingContext ctx, ScaffoldModel model)
        {
            _templatedElement = null;
            _fullpath = null;
            var projectname = Tokenize(ProjectName, ctx, model);
            _project = ctx.DTE.GetProject(projectname);
            if (_project == null)
            {
                ctx.Output.WriteLine("Project '" + projectname + "' not found. The project name must correspond to the name that appear in Visual Studio.");
                return;
            }

            var prjnamespace = _project.Properties.Item("DefaultNamespace").Value as string;
            var prjDir = Path.GetDirectoryName(_project.FileName);
            var templatename = Tokenize(TemplateName, ctx, model);
            var templatepath = Path.Combine(ctx.SolutionFolder, templatename);

            if (!File.Exists(templatepath))
            {
                ScaffoldingError err = new ScaffoldingError()
                    { TemplateFile = templatepath, Message = "template file not found " };
                ctx.Errors.Add(err);
                ctx.Output.WriteLine(err.ToString());
                return;
            }
            
            var outputPath = Tokenize(OutputName, ctx, model);
            model.GeneratedNamespace = prjnamespace + "." + Path.GetDirectoryName(outputPath).ToNamespace();

            _fullfilename = Path.Combine(prjDir, outputPath);
            _fullpath = Path.GetDirectoryName(_fullfilename);

            if (File.Exists(_fullfilename))
            {
                if (!ctx.Force)
                {
                    ctx.Output.WriteLine("The destination file " + _fullfilename + " already exists and was not overriden.");
                    return;
                }
                
                var eltItem = ctx.DTE.Solution.FindProjectItem(_fullfilename);
                //We call save to checkout the file, if under source control
                if (eltItem != null)
                {
                    var nfo = new FileInfo(_fullfilename);
                    if ((nfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                    {
                        bool wasOpened = eltItem.IsOpen;
                        var wnd = eltItem.Open();
                        wnd.Document.Save();
                        if (!wasOpened)
                            wnd.Close();
                    }                   
                }
            }
            
            string content = ctx.ApplyTemplate(templatepath);
            if (content == "ErrorGeneratingOutput") 
                return;

            _templatedElement = content;
        }

        internal void OutputTemplate(ScaffoldingContext ctx)
        {
            if (_templatedElement == null) return;

            if (!Directory.Exists(this._fullpath))
                Directory.CreateDirectory(this._fullpath);
            
            ctx.Output.WriteLine("Generating file " + _fullfilename);

            using (StreamWriter writer = new StreamWriter(this._fullfilename, false))
            {
                writer.Write(_templatedElement);
            }

            var eltItem = ctx.DTE.Solution.FindProjectItem(_fullfilename);
            if (eltItem == null)
            {
                _project.ProjectItems.AddFromFile(this._fullfilename);
            }
        }
    }
}
